package com.yedean.factory.context;

import com.yedean.factory.BeansException;
import com.yedean.factory.ConfigurableListableBeanFactory;
import com.yedean.factory.config.BeanFactoryPostProcessor;
import com.yedean.factory.config.BeanPostProcessor;
import com.yedean.factory.context.event.ApplicationEventMulticaster;
import com.yedean.factory.context.event.ContextClosedEvent;
import com.yedean.factory.context.event.SimpleApplicationEventMulticaster;
import com.yedean.factory.io.DefaultResourceLoader;

import java.util.Collection;
import java.util.Map;

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {


    public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";

    private ApplicationEventMulticaster applicationEventMulticaster;

    @Override
    public void refresh() throws BeansException {
        //1.刷新创建容器 并加载BeanDefiniton
        refreshBeanFactory();
        //2.获取beanFactory
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        //3.把感知器添加进去
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
        //4.执行beanFactoryPostProcessor
        invokeBeanFactoryPostProcessor(beanFactory);
        //5.提前注册beanPostProcessor
        registerBeanPostProcessor(beanFactory);
        //6.初始化事件多播器
        initApplicationEventMulticaster();
        //7.注册监听器
        registerApplicationListener(beanFactory);
        //8.提前实例化单例bean对象
        beanFactory.preInstantiateSingletons();
        //9.发布容器刷新完成事件
        finishRefresh();


    }

    private void finishRefresh() {
        publishEvent(new ContextClosedEvent(this));
    }

    @Override
    public void publishEvent(ApplicationEvent event) {
        this.applicationEventMulticaster.multicastEvent(event);
    }

    private void registerApplicationListener(ConfigurableListableBeanFactory beanFactory) {
        Collection<ApplicationListener> listeners = beanFactory.getBeansOfType(ApplicationListener.class).values();
        listeners.forEach(applicationEventMulticaster::addApplicationListener);
    }

    private void initApplicationEventMulticaster() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, applicationEventMulticaster);
    }

    private void registerBeanPostProcessor(ConfigurableListableBeanFactory beanFactory) {
        Map<String, BeanPostProcessor> beanPostProcessorMap = getBeansOfType(BeanPostProcessor.class);
        beanPostProcessorMap.forEach((x, y) -> {
            beanFactory.addBeanPostProcessor(y);
        });
    }

    private void invokeBeanFactoryPostProcessor(ConfigurableListableBeanFactory beanFactory) {
        Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = getBeansOfType(BeanFactoryPostProcessor.class);
        beanFactoryPostProcessorMap.forEach((x, y) -> {
            y.postProcessBeanFactory(beanFactory);
        });
    }


    /**
     * 创建容器并且加载定义
     *
     * @throws BeansException
     */
    protected abstract void refreshBeanFactory() throws BeansException;


    /**
     * 获取bean工厂
     *
     * @return ConfigurableListableBeanFactory
     */
    protected abstract ConfigurableListableBeanFactory getBeanFactory();

    @Override
    public <T> Map<String, T> getBeansOfType(Class<T> tClass) {
        return getBeanFactory().getBeansOfType(tClass);
    }

    @Override
    public String[] getBeanDefinitionNames() {
        return getBeanFactory().getBeanDefinitionNames();
    }

    @Override
    public Object getBean(String name) {
        return getBeanFactory().getBean(name);
    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        return getBeanFactory().getBean(name, requiredType);
    }

    @Override
    public <T> T getBean(Class<T> requireType) {
        return getBeanFactory().getBean(requireType);
    }

    @Override
    public Object getBean(String name, Object... args) {
        return getBeanFactory().getBean(name, args);
    }

    @Override
    public void registerShutdownHook() {
        Runtime.getRuntime().addShutdownHook(new Thread(this::close));
    }

    @Override
    public void close() {
        publishEvent(new ContextClosedEvent(this));
        getBeanFactory().destroySingletons();
    }
}
